package com.devops.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.devops.admin.model.DOSClusterInfo;
import com.devops.admin.model.DOSRelease;
import com.devops.admin.po.DOSK8sDeployment;
import com.devops.admin.service.*;
import com.devops.admin.util.Assert;
import com.devops.admin.util.K8sClientUtil;
import com.devops.admin.util.TimeUtil;
import com.devops.admin.vo.DOSDeploymentVo;
import com.devops.admin.vo.DOSNamespaceChart;
import com.devops.constant.DevOpsConstant;
import com.devops.shiro.vm.BaseUserVM;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.apps.*;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.RollableScalableResource;

import org.apache.shiro.SecurityUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName DOSK8sDeploymentServiceImpl
 * @Description TODO
 * @Author hepingguo
 * @Date 2022/5/20 08:44
 * @Version 1.0
 **/
@Service
public class DOSK8sDeploymentServiceImpl implements DOSK8sDeploymentService {

    @Autowired
    private DOSReleaseService dosReleaseService;
    @Autowired
    DOSReleaseOutService dosReleaseOutService;
    @Resource
    DOSClusterInfoService dosClusterInfoService;

    @Override
    public DOSNamespaceChart dealDbDeployment(DOSDeploymentVo deployment) {
        Assert.isTrueMvc(deployment.getId() != null && deployment.getId() > 0,"releaseId不能为空");
        DOSNamespaceChart namespaceChart;
        if (deployment.getType() == 1) {
            namespaceChart = dosReleaseOutService.getInfoByReleaseId(deployment.getId());
        } else {
            namespaceChart = dosReleaseOutService.getInfoByResourceId(deployment.getId());
        }
        Assert.isTrueMvc(namespaceChart != null
                && StrUtil.isNotBlank(namespaceChart.getNamespace()),"请检查releaseId或者kubernetesResourceId是否正确");
        namespaceChart.setReleaseType(deployment.getType());
        return namespaceChart;
    }

    @Override
    public List<DOSK8sDeployment> getK8sDeploymentInfo(DOSNamespaceChart namespaceChart) {
        if (namespaceChart.getClient() == null) {
            KubernetesClient client = K8sClientUtil.getKubernetesClient(namespaceChart.getConfigPath(),namespaceChart.getConfigContent());
            namespaceChart.setClient(client);
        }

        //通过release和命名空间过滤deployment
        List<Deployment> deploymentList = new ArrayList<>();
        NonNamespaceOperation<Deployment, DeploymentList, RollableScalableResource<Deployment>> operation =
                namespaceChart.getClient().apps().deployments().inNamespace(namespaceChart.getNamespace());

        if (namespaceChart.getReleaseType() != null && namespaceChart.getReleaseType().intValue() == DevOpsConstant.ReleaseType.YAML.intValue()){
            // 通过yaml方式部署的查询方式
            Deployment deployment = operation.withName(namespaceChart.getName()).get();
            deploymentList.add(deployment);
        } else {
            // 通过helm方式部署的查询方式
            deploymentList =  operation.withLabel(DevOpsConstant.K8sConfig.POD_LABEL_RELEASE_NAME, namespaceChart.getReleaseName())
                    .withLabel(DevOpsConstant.K8sConfig.POD_LABEL_CHART_NAME, namespaceChart.getChartName()).list().getItems();
        }

        List<DOSK8sDeployment> deployments = new ArrayList<>();
        if (CollectionUtil.isEmpty(deploymentList) || deploymentList.size() < 1){
            return deployments;
        }
        //获取目标属性进行赋值
        deploymentList.forEach(deployment->{
            if (deployment == null){
                return;
            }
            ObjectMeta metadata = deployment.getMetadata();
            DeploymentSpec spec = deployment.getSpec();
            DeploymentStatus status = deployment.getStatus();

            //创建时间
            String created = TimeUtil.getDate(metadata.getCreationTimestamp());
            //组装labels
            String[] labels = K8sClientUtil.dealMapToStringArray(metadata.getLabels());
            //组装annotations
            String[] annotations = K8sClientUtil.dealMapToStringArray(metadata.getAnnotations());

            String name = metadata.getName();
            String namespace = namespaceChart.getNamespace();
            String strategyType = spec.getStrategy().getType();

            List<DeploymentCondition> conditions = status.getConditions();
            //将时间转换为本地时间
            conditions.forEach(con->{
                if (StrUtil.isNotBlank(con.getLastUpdateTime())){
                    con.setLastUpdateTime(TimeUtil.getDate(con.getLastUpdateTime()));
                }
                if (StrUtil.isNotBlank(con.getLastTransitionTime())){
                    con.setLastTransitionTime(TimeUtil.getDate(con.getLastTransitionTime()));
                }
            });
            //组装selector
            String[] selector = K8sClientUtil.dealMapToStringArray(spec.getSelector().getMatchLabels());
            List<String> replicasList = setReplicas(status);
            DOSK8sDeployment dosDeployment = DOSK8sDeployment.builder().created(created).name(name)
                    .namespace(namespace).labels(labels).annotations(annotations).replicasStr(String.join(",",replicasList))
                    .replicas(status.getReplicas())
                    .selector(selector).strategyType(strategyType).conditions(conditions)
                    .scale(status.getReplicas()).build();

            deployments.add(dosDeployment);
        });
        return deployments;
    }

    @NotNull
    private List<String> setReplicas(DeploymentStatus status) {
        List<String> replicasList = new ArrayList<>();
        Integer replicas = status.getReplicas() != null ? status.getReplicas() : 0;
        Integer availableReplicas = status.getAvailableReplicas() != null ? status.getAvailableReplicas() : 0;
        Integer unavailableReplicas = status.getUnavailableReplicas() != null ? status.getUnavailableReplicas() : 0;
        Integer readyReplicas = status.getReadyReplicas() != null ? status.getReadyReplicas() : 0;
        Integer updatedReplicas = status.getUpdatedReplicas() != null ? status.getUpdatedReplicas() : 0;
        replicasList.add(replicas + " " + DevOpsConstant.SuffixType.TOTAL);
        replicasList.add(availableReplicas + " " + DevOpsConstant.SuffixType.AVAILABLE);
        replicasList.add(unavailableReplicas + " " + DevOpsConstant.SuffixType.UNAVAILABLE);
        replicasList.add(readyReplicas + " " + DevOpsConstant.SuffixType.READY);
        replicasList.add(updatedReplicas + " " + DevOpsConstant.SuffixType.UPDATED);
        return replicasList;
    }

    @Override
    public boolean scaleDeployment(int releaseId, int scale, String deploymentName,Integer type) {
        Assert.isTrueMvc(scale >= 0,"扩容参数scale不能为空");
        DOSNamespaceChart namespaceChart = getDosNamespaceChart(releaseId, deploymentName,type);
        KubernetesClient client = K8sClientUtil.getKubernetesClient(namespaceChart.getConfigPath(),namespaceChart.getConfigContent());
        client.apps().deployments().inNamespace(namespaceChart.getNamespace())
                .withName(deploymentName).scale(scale);

        return true;
    }

    @NotNull
    private DOSNamespaceChart getDosNamespaceChart(int releaseId, String deploymentName,Integer type) {
        checkUser();
        Assert.isTrueMvc(releaseId > 0,"releaseId不能为空");
        Assert.isTrueMvc(StrUtil.isNotBlank(deploymentName),"deploymentName不能为空");
        DOSNamespaceChart namespaceChart;
        if (type == 1) {
            namespaceChart = dosReleaseOutService.getInfoByReleaseId(releaseId);
        }else{
            namespaceChart = dosReleaseOutService.getInfoByResourceId(releaseId);
        }
        Assert.isTrueMvc(null!=namespaceChart,"请检查releaseId是否正确");
        return namespaceChart;
    }

    @Override
    public boolean restartDeployment(int releaseId, String deploymentName,Integer type) {
        DOSNamespaceChart dosNamespaceChart = getDosNamespaceChart(releaseId, deploymentName,type);
        KubernetesClient client = K8sClientUtil.getKubernetesClient(dosNamespaceChart.getConfigPath(),dosNamespaceChart.getConfigContent());
        client.apps().deployments().inNamespace(dosNamespaceChart.getNamespace())
                .withName(deploymentName)
                .rolling()
                .restart();
        return true;
    }

    @Override
    public boolean deleteDeployment(int releaseId, String deploymentName,Integer type) {
        DOSNamespaceChart dosNamespaceChart = getDosNamespaceChart(releaseId, deploymentName,type);
        KubernetesClient client = K8sClientUtil.getKubernetesClient(dosNamespaceChart.getConfigPath(),dosNamespaceChart.getConfigContent());
        return !client.apps().deployments().inNamespace(dosNamespaceChart.getNamespace())
                .withName(deploymentName)
                .delete()
                .isEmpty();
    }

    @Override
    public boolean restartRelease(Integer releaseId) throws IOException {
        Assert.isTrueMvc(releaseId != null && releaseId > 0,"releaseId不能为空");
        DOSNamespaceChart namespaceChart = dosReleaseOutService.getInfoByReleaseId(releaseId);
        Assert.isTrueMvc(namespaceChart != null && StrUtil.isNotBlank(namespaceChart.getNamespace()),"请检查releaseId是否正确");
        List<DOSK8sDeployment> deployments = this.getK8sDeploymentInfo(namespaceChart);
        if (CollectionUtil.isNotEmpty(deployments)){
            DOSK8sDeployment dosDeployment = deployments.get(0);
            KubernetesClient client = namespaceChart.getClient();
            client.apps().deployments().inNamespace(namespaceChart.getNamespace())
                    .withName(dosDeployment.getName())
                    .rolling()
                    .restart();
            return true;
        }
        return false;
    }

    @Override
    public boolean restartReleaseByName(String releaseName) throws IOException {
        Assert.isTrueMvc(StrUtil.isNotBlank(releaseName),"releaseName不能为空");
        DOSRelease release = dosReleaseService.getOne(new LambdaQueryWrapper<DOSRelease>().eq((DOSRelease::getReleaseName),releaseName));
        Assert.isTrueMvc(release != null && release.getId() != null,"未查询到相关release信息，请检查releaseName");
        return restartRelease(release.getId());
    }

    @Override
    public List<DOSK8sDeployment> queryDeploymentInfo(int clusterId, String deploymentName) {
        DOSClusterInfo cluster = dosClusterInfoService.getById(clusterId);
        KubernetesClient client = K8sClientUtil.getKubernetesClient(cluster.getConfigPath(),cluster.getConfigContent());
        return client.apps().deployments().inAnyNamespace().list().getItems().stream()
                .filter(deployment -> deployment.getMetadata().getName().contains(deploymentName))
                .map(deployment -> DOSK8sDeployment.builder()
                        .name(deployment.getMetadata().getName())
                        .namespace(deployment.getMetadata().getNamespace())
                        .build())
                .collect(Collectors.toList());
    }

    @Override
    public void restart(int clusterId, String namespace, String deploymentName) {
        DOSClusterInfo cluster = dosClusterInfoService.getById(clusterId);
        KubernetesClient client = K8sClientUtil.getKubernetesClient(cluster.getConfigPath(),cluster.getConfigContent());
        client.apps().deployments().inNamespace(namespace).withName(deploymentName).rolling().restart();
    }

    /**
     * @Author heyabo
     * @Description 用户认证
     * @Date 13:39 2022/5/20
     * @Param []
     **/
    private void checkUser(){
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        Assert.isTrueMvc(null!=user,"用户未认证");
    }

}
